iT邦幫忙

2025 iThome 鐵人賽

DAY 15
0
DevOps

Mac 環境 n8n 30 天學習計畫系列 第 15

Day 15:JSON 處理(解析溫度資訊)📑

  • 分享至 

  • xImage
  •  

一、先決條件

  1. 已有一個 workflow,並且上一個節點(例如 HTTP Request)能回傳天氣 JSON,且該節點連到這個 Function 節點(本節點接收 items)。
  2. 方便測試可先用 Manual Trigger → HTTP Request(或 Set)→ Function 的流程。
    https://ithelp.ithome.com.tw/upload/images/20250927/20169144W9K9D6NCPX.png

二、Function Node

https://ithelp.ithome.com.tw/upload/images/20250927/20169144Lqg3nIAP6r.png

// Day15 - 解析溫度資訊(支援多種 API 結構)
// 回傳內容會包含:temp_c, temp_f, message,以及若有 hourly 則會有 min/max/avg

function toNumber(v){
  const n = Number(v);
  return (Number.isFinite(n)) ? n : null;
}

// heuristic 將 raw 溫度轉為攝氏(簡單判斷:>200 => K, >70 => F, 否則視為 C)
function rawToCelsius(raw){
  const n = toNumber(raw);
  if (n === null) return null;
  if (n > 200) return n - 273.15;           // 很可能是 Kelvin
  if (n > 70) return (n - 32) * 5/9;        // 很可能是 Fahrenheit
  return n;                                // 否則視為 Celsius
}

function round1(v){
  if (v === null || v === undefined) return null;
  return Math.round(v*10)/10;
}

return items.map(item => {
  try {
    const data = item.json || {};
    // 優先取常見位置
    // Open-Meteo: data.current_weather.temperature
    // OpenWeatherMap: data.main.temp
    // Generic: data.temperature
    let tempRaw = null;
    let source = 'unknown';
    if (data.current_weather && data.current_weather.temperature !== undefined){
      tempRaw = data.current_weather.temperature;
      source = 'open-meteo:current_weather';
    } else if (data.main && data.main.temp !== undefined){
      tempRaw = data.main.temp;
      source = 'openweathermap:main.temp';
    } else if (data.temperature !== undefined){
      tempRaw = data.temperature;
      source = 'generic:temperature';
    }

    // 若有 hourly 陣列(例如 open-meteo 的 hourly.temperature_2m),算 min/max/avg
    let hourlyStats = null;
    if (data.hourly && Array.isArray(data.hourly.temperature_2m) && data.hourly.temperature_2m.length > 0){
      const arr = data.hourly.temperature_2m.map(v => toNumber(v)).filter(v => v !== null);
      if (arr.length > 0){
        const sum = arr.reduce((s,x)=>s+x,0);
        const avg = sum / arr.length;
        const min = Math.min(...arr);
        const max = Math.max(...arr);
        hourlyStats = {
          min_c: round1(rawToCelsius(min)),
          max_c: round1(rawToCelsius(max)),
          avg_c: round1(rawToCelsius(avg)),
          raw_count: arr.length
        };
      }
    }

    // 如果沒有直接 tempRaw,但有 hourly 可從 current index 或第一筆取
    if (tempRaw === null && data.current_weather && data.current_weather.temperature !== undefined){
      tempRaw = data.current_weather.temperature;
      source = 'open-meteo:current_weather';
    }

    // 解析 temp
    const tempC = (tempRaw !== null) ? round1(rawToCelsius(tempRaw)) : null;
    const tempF = (tempC !== null) ? round1(tempC * 9/5 + 32) : null;

    // 取得時間、地點等可讀欄位(盡量找出現有資料)
    const time = (data.current_weather && data.current_weather.time) || data.dt || data.time || null;
    const lat = data.latitude || (data.location && data.location.lat) || null;
    const lon = data.longitude || (data.location && data.location.lon) || null;

    // 組成可讀訊息(用 template literal 保留換行)
    let msgLines = [];
    if (lat !== null && lon !== null) msgLines.push(`地點: (${lat}, ${lon})`);
    if (time) msgLines.push(`時間: ${time}`);
    if (tempC !== null) msgLines.push(`現在溫度: ${tempC}°C (${tempF}°F)`);
    else msgLines.push(`現在溫度: N/A`);
    if (hourlyStats){
      msgLines.push(`今日(hourly)最小: ${hourlyStats.min_c}°C,最大: ${hourlyStats.max_c}°C,平均: ${hourlyStats.avg_c}°C`);
    }
    msgLines.push(`原始來源: ${source}`);

    const message = msgLines.join('\n'); // 保留換行

    // 回傳結構:message(可直接顯示或寄發)、以及個別欄位方便後續使用
    return {
      json: {
        temp_raw: tempRaw,
        temp_c: tempC,
        temp_f: tempF,
        hourly: hourlyStats,
        message,
        raw: data
      }
    };
  } catch (err){
    return {
      json: {
        error: true,
        message: `解析錯誤: ${err.message}`,
        raw: item.json
      }
    };
  }
});

三、在 n8n 中逐步執行

  1. 打開 workflow(例如 Day14_Weather_API),在 Get Weather 節點後新增一個 Function 節點,命名 Parse Temp

  2. 把上面程式碼貼進 Parse Temp 的程式碼區(完全取代預設內容)。

  3. Get Weather 的輸出拉線連到 Parse Temp

  4. 在畫面右上點 Save

  5. 測試方法:

    • 若上游是 Manual Trigger → 點 Execute Workflow
    • 若上游是單獨的 HTTP Request node,直接在該 node 按 Execute Node,然後再執行 Function node(或整個 workflow)。
  6. 開啟 Executions / Execution Log → 點剛剛的執行 → 展開 Parse TempOutput → 會看到 message 欄位內有多行可讀文字(如果 UI 有顯示為單行帶 \n,可以點開值或複製貼到記事本確認換行;外部寄信 / Telegram / Slack 等通常會正確呈現換行)。


五、input/ouput

https://ithelp.ithome.com.tw/upload/images/20250927/20169144n7RpYo3dQn.png
input (Open-Meteo)

[
  {
    "latitude": 25,
    "longitude": 121.625,
    "generationtime_ms": 0.037670135498046875,
    "utc_offset_seconds": 28800,
    "timezone": "Asia/Taipei",
    "timezone_abbreviation": "GMT+8",
    "elevation": 12,
    "current_weather_units": {
      "time": "iso8601",
      "interval": "seconds",
      "temperature": "°C",
      "windspeed": "km/h",
      "winddirection": "°",
      "is_day": "",
      "weathercode": "wmo code"
    },
    "current_weather": {
      "time": "2025-09-27T21:30",
      "interval": 900,
      "temperature": 27.6,
      "windspeed": 6.8,
      "winddirection": 87,
      "is_day": 0,
      "weathercode": 1
    }
  }
]

Function output

[
  {
    "temp_raw": 27.6,
    "temp_c": 27.6,
    "temp_f": 81.7,
    "hourly": null,
    "message": "地點: (25, 121.625)\n時間: 2025-09-27T21:30\n現在溫度: 27.6°C (81.7°F)\n原始來源: open-meteo:current_weather",
    "raw": {
      "latitude": 25,
      "longitude": 121.625,
      "generationtime_ms": 0.037670135498046875,
      "utc_offset_seconds": 28800,
      "timezone": "Asia/Taipei",
      "timezone_abbreviation": "GMT+8",
      "elevation": 12,
      "current_weather_units": {
        "time": "iso8601",
        "interval": "seconds",
        "temperature": "°C",
        "windspeed": "km/h",
        "winddirection": "°",
        "is_day": "",
        "weathercode": "wmo code"
      },
      "current_weather": {
        "time": "2025-09-27T21:30",
        "interval": 900,
        "temperature": 27.6,
        "windspeed": 6.8,
        "winddirection": 87,
        "is_day": 0,
        "weathercode": 1
      }
    }
  }
]

在 Email / Telegram 發送 message 時會看到真正換行效果。


上一篇
Day 14 — 呼叫天氣 API → Console🌥️
系列文
Mac 環境 n8n 30 天學習計畫15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言